import utils from '../core/utils';
import logger from '../core/logger';
import apiInfo from './core/apiInfo';
import apiProvider from './core/apiProvider';
import { clientBase } from './core/clientBase';
import { danmakuClient } from './danmakuClient';
import { playMode } from '../model/common/enums';
import playModel from '../model/videos/playModel';
import settingHelper from '../core/settingHelper';
import returnModel from '../model/common/returnModel';
import playerModel from '../model/videos/playerModel';
import flvPlayerUrlModel from '../model/videos/flvPlayerUrlModel';

export class playUrlClient extends clientBase {
  private danmaku: danmakuClient;

  constructor() {
    super();
    this.danmaku = new danmakuClient();
  }

  loadVideo(model: playerModel, qn: number, uid: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let pm: returnModel<playModel> = null;
        if (!utils.isEmpty(model)) {
          let xml = await this.danmaku.getBiliBili(model.mid);
          switch (model.mode) {
            case playMode.movie:
            case playMode.bangumi:
            case playMode.vipBangumi:
              pm = await this.getBangumiUrl(model, qn);
            case playMode.video:
              pm = await this.getVideoUrl(model.aid, model.mid, uid, qn);
              break;
            case playMode.local:
            case playMode.formLocal:
              break;
          }
          if (!utils.isEmpty(pm))
            pm.data.danmakuXml = xml.data;
        }
        if (pm.success)
          resolve(self.buildSuccessResult<playModel>(pm.data));
        else
          resolve(self.buildFailedResult<playModel>(pm.msg));
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  getBangumiUrl(model: playerModel, qn: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let pm: returnModel<playModel> = null;
        if (qn < 10) {
          switch (qn) {
            case 1:
              qn = 32;
              break;
            case 2:
              qn = 64;
              break;
            case 3:
              qn = 80;
              break;
            case 4:
              qn = 116;
              break;
            default:
              qn = 64;
              break;
          }
        }
        if (settingHelper.useDASH)
          pm = await this.getBilibiliBangumiUrlDash(model, qn);
        if (utils.isEmpty(pm) || !pm.success || !pm.data.success)
          pm = await this.getBilibiliBangumiUrl(model, qn);
        if (utils.isEmpty(pm) || !pm.success || !pm.data.success)
          pm = await this.get23MoeUrl(model);
        if (utils.isEmpty(pm) || !pm.success || !pm.data.success)
          pm = await this.getBiliPlus(model, qn);
        if (!utils.isEmpty(pm) && pm.success && pm.data.success)
          resolve(self.buildSuccessResult<playModel>(pm.data));
        else
          resolve(self.buildFailedResult<playModel>(pm.msg));
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getBilibiliBangumiUrlDash(model: playerModel, qn: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let info = new apiInfo("https://api.bilibili.com/pgc/player/web/playurl", `cid=${model.mid}&otype=json&type=&module=bangumi&season_type=${model.seasonType}&qn=${qn}&fourk=1&fnver=0&fnval=4048`, null, true);
        info.buildVer = apiProvider.Build6;
        this.client.getResult(info).then((res) => {
          if (res.code == 0) {
            let data = this.getDashInfo(res, qn);
            resolve(self.buildSuccessResult<playModel>(data));
          } else
            resolve(self.buildFailedResult<playModel>(res.message));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getBilibiliBangumiUrl(model: playerModel, qn: number): Promise<returnModel<playModel>> {
    var self = this;
    let pm: playModel = new playModel();
    return new Promise(async (resolve) => {
      try {
        let info = new apiInfo("https://api.bilibili.com/pgc/player/web/playurl",
          `cid=${model.mid}&otype=json&type=&module=bangumi&season_type=${model.seasonType}&qn=${qn}`, null, true);
        info.buildVer = apiProvider.Build6;
        this.client.getResult(info).then(async (res) => {
          if (res.code == 0) {
            let data: flvPlayerUrlModel = res;
            if (data.code == 0) {
              pm.success = true;
              if (data?.result?.durl?.length > 0)
                data.result.durl.forEach((i) => pm.urls.push(i.url));
              resolve(self.buildSuccessResult<playModel>(pm));
            } else {
              info = new apiInfo("https://api.bilibili.com/pgc/player/web/playurl", `&cid=${model.mid}&qn=${qn}&type=&otype=json&module=bangumi&season_type=${model.seasonType}`, null, true);
              let ret = await this.client.getResult(info);
              if (ret.code == 0) {
                data = res.result;
                pm.success = true;
                if (data?.data?.durl?.length > 0)
                  data.data.durl.forEach((i) => pm.urls.push(i.url));
                resolve(self.buildSuccessResult<playModel>(pm));
              } else
                resolve(self.buildFailedResult<playModel>(ret.message));
            }
          } else
            resolve(self.buildFailedResult<playModel>(res.message));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private get23MoeUrl(model: playerModel): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let url = `https://moe.nsapps.cn/api/v1/BiliAnimeUrl?animeid=${model.banId}&cid=${model.mid}&epid=${model.banInfo.episode_id}&rnd=${apiProvider.timeSpanSeconds}`;
        this.client.getResult(url).then((res) => {
          if (res.code == 0 && res.mode == 'mp4') {
            let pm: playModel = new playModel();
            let data = ((res.data as Array<any>)[0]).url.toString();
            pm.urls.push(data);
            resolve(self.buildSuccessResult<playModel>(pm));
          } else
            resolve(self.buildFailedResult<playModel>(res.message));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getBiliPlus(model: playerModel, qn: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        this.getBiliPlusUrl(parseFloat(model.episodeId), qn, model.seasonType).then(async (res) => {
          if (res.success) {
            if (res.data.success)
              resolve(self.buildSuccessResult<playModel>(res.data));
            else {
              let ret = await this.getBiliPlusUrl2(model);
              if (ret.success) {
                resolve(self.buildSuccessResult<playModel>(ret.data));
              } else
                resolve(self.buildFailedResult<playModel>(ret.msg));
            }
          } else
            resolve(self.buildFailedResult<playModel>(res.msg));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getBiliPlusUrl2(model: playerModel): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let url = `https://www.biliplus.com//BPplayurl.php?cid=${model.mid}&otype=json`;
        this.client.getResult(url).then((res) => {
          if (res.code == 0) {
            let model = new playModel();
            (res.durl as Array<any>).forEach((i) => model.urls.push(i.utl.toString()));
            resolve(self.buildSuccessResult<playModel>(model));
          } else
            resolve(self.buildFailedResult<playModel>(res.message));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  getVideoUrl(aid: string, cid: number, uid: number, qn: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let pm: returnModel<playModel>;
        if (qn < 10) {
          switch (qn) {
            case 1:
              qn = 32;
              break;
            case 2:
              qn = 64;
              break;
            case 3:
              qn = 80;
              break;
            case 4:
              qn = 116;
              break;
            default:
              qn = 64;
              break;
          }
        }
        if (settingHelper.useDASH)
          pm = await this.getVideoUrlDASH(aid, cid, uid, qn);
        if (utils.isEmpty(pm) || !pm.success || !pm.data.success)
          pm = await this.getVideoUrlV1(aid, cid, uid, qn);
        if (utils.isEmpty(pm) || !pm.success || !pm.data.success)
          pm = await this.getBiliPlusUrl(cid, qn, 0);
        if (!utils.isEmpty(pm) && pm.success && pm.data.success)
          resolve(self.buildSuccessResult<playModel>(pm.data));
        else
          resolve(self.buildFailedResult<playModel>(pm.msg));
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getVideoUrlDASH(aid: string, cid: number, uid: number, qn: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let info = new apiInfo("https://api.bilibili.com/x/player/playurl", `avid=${aid}&cid=${cid}&qn=${qn}&type=&otype=json&fourk=1&fnver=0&fnval=4048&mid=${uid}`, null, true);
        info.buildVer = apiProvider.Build6;
        this.client.getResult(info).then((res) => {
          if (res.code == 0) {
            let data = this.getDashInfo(res, qn);
            resolve(self.buildSuccessResult<playModel>(data));
          } else
            resolve(self.buildFailedResult<playModel>(res.message));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getVideoUrlV1(aid: string, cid: number, uid: number, qn: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let info = new apiInfo("https://api.bilibili.com/x/player/playurl", `avid=${aid}&cid=${cid}&qn=${qn}&type=&otype=json&mid=${uid}`, null, true);
        info.buildVer = apiProvider.Build6;
        this.client.getResult(info).then((res) => {
          if (res.code == 0) {
            let pm: playModel = new playModel();
            pm.success = true;
            if (res.data?.durl?.length > 0)
              res.data.durl.forEach((i) => pm.urls.push(i.url));
            resolve(self.buildSuccessResult<playModel>(pm));
          } else
            resolve(self.buildFailedResult<playModel>(res.message));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getBiliPlusUrl(cid: number, qn: number, season_type: number): Promise<returnModel<playModel>> {
    var self = this;
    return new Promise(async (resolve) => {
      try {
        let url = `https://www.biliplus.com/BPplayurl.php?appkey=${apiProvider.WebVideoKey.appkey}&cid=${cid}&qn=${qn}&type=&otype=json&module=bangumi&season_type=${season_type}`;
        this.client.getResult(url).then((res) => {
          if (res.code == 0) {
            let pm: playModel = new playModel();
            pm.success = true;
            if (res.data?.durl?.length > 0)
              res.data?.durl.forEach((i) => pm.urls.push(i.url));
            resolve(self.buildSuccessResult<playModel>(pm));
          } else
            resolve(self.buildFailedResult<playModel>(res.message));
        });
      } catch (e) {
        logger.error(e);
        resolve(super.buildExceptionResult<playModel>(e));
      }
    });
  }

  private getDashInfo(res: any, qn: number): playModel {
    let pm: playModel = new playModel();
    let video: Array<any>, audio: Array<any>;
    try {
      video = res.data.dash.video;
      audio = res.data.dash.audio;
    } catch {
      try {
        video = res.result.dash.video;
        audio = res.result.dash.audio;
      } catch {
        try {
          video = res.dash.video;
          audio = res.dash.audio;
        } catch {

        }
      }
    }
    if (!utils.isEmpty(audio))
      pm.urls.push(audio[0]);
    let codecid = settingHelper.useHEVC ? 12 : 7;
    if (!utils.isEmpty(video)) {
      let arr = video.filter((i) => i.codecid == codecid && i.id == qn);
      if (utils.isEmpty(arr) || arr.length == 0) {
        codecid = 7;
        arr = video.filter((i) => i.codecid == codecid && i.id == qn);
      }
      pm.success = true;
      pm.urls.push(arr[0]);
    }
    return pm;
  }
}
export default new playUrlClient();